package concurrency.deadlock;

/**
 * Created by Tzachi on 18/10/2014.
 */
public class SynchronizedBlock {
    static class Account {
        double balance;
        int id;

        public Account(int id, double balance) {
            this.balance = balance;
            this.id = id;
        }

        void withdraw(double amount) {
            balance -= amount;
        }

        void deposit(double amount) {
            balance += amount;
        }

        public int compareTo(Account second) {
            return this.id - second.id;
        }
    }

    @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
    public static void blockingTransfer(Account from, Account to, double amount) {
        synchronized (from) {
            synchronized (to) {
                from.withdraw(amount);
                to.deposit(amount);
            }
        }
    }

    /*
        non-blocking is achieved by ordering
     */
    public static void nonblockingTransfer(Account from, Account to, double amount) {
        Account first = from;
        Account second = to;
        if (first.compareTo(second) < 0) {
            // Swap them
            first = to;
            second = from;
        }
        synchronized (first) {
            synchronized (second) {
                from.withdraw(amount);
                to.deposit(amount);
            }
        }
    }

    public static void main(String[] args) {
        final Account a = new Account(1, 1000);
        final Account b = new Account(2, 300);
        Thread threadA = new Thread() {
            public void run() {
                blockingTransfer(a, b, 200);
            }
        };
        Thread threadB = new Thread() {
            public void run() {
                blockingTransfer(b, a, 300);
            }
        };
        threadA.start();
        threadB.start();
    }
}
